
#include <cstdio>
#include <ctime>

#include <BMap>
#include <BIcon>
#include <BImage>
#include <BDisk>
#include <BScreen>
#include <BSystem>
#include <BFolder>
#include <BEntry>
#include "member_BScreen.h"
#include "member_BDisk.h"
#include "IGlobal.h"

using namespace BWE;

#ifdef linux
#include <unistd.h>
#include <mntent.h>
#include <sys/types.h> 
#include <X11/Xlib.h>
#include <X11/keysymdef.h>
#include <X11/XKBlib.h>
#include <pwd.h> 

const BArray<BScreenHolder>& GetSystemScreens()
{
	static BArray<BScreenHolder> screens;
	static bool inited = false;
	if (!inited && app_display)
	{
		inited = true;
		int num = XScreenCount(app_display);
		for(int i = 0; i < num; i++)
		{
			Screen* scn = XScreenOfDisplay(app_display, i);
			BScreen* screen = new BScreen();
			screen->setName(i);
			screen_member(screen)->info = XDisplayName(0);
			screen_member(screen)->number = i;
			if (scn)
			{
				screen_member(screen)->pos.x() = 0;
				screen_member(screen)->pos.y() = 0;
				screen_member(screen)->size.setWidth(XWidthOfScreen(scn));
				screen_member(screen)->size.setHeight(XHeightOfScreen(scn));
				screen_member(screen)->pixelBits = 32;
				//printf("screen %d : %d, %d, %d, %d\n", i, deviceMode.dmPosition.x, deviceMode.dmPosition.y, deviceMode.dmPelsWidth, deviceMode.dmPelsHeight);
			}
			else
			{
				screen_member(screen)->pos.x() = 0;
				screen_member(screen)->pos.y() = 0;
				screen_member(screen)->size.setWidth(0);
				screen_member(screen)->size.setHeight(0);
				screen_member(screen)->pixelBits = 0;
			}
			screens.append(screen);
		}
	}
	return screens;
}

int BSystem::GetCupNumber()
{
	static int CpuNumber = 0;

	return CpuNumber;
}

const BArray<BDiskHolder>& GetSystemDisks()
{
	static BArray<BDiskHolder> disks;
	static bool inited = false;
	if (!inited)
	{
		BDisk* root = new BDisk();
		disk_member(root)->type = BDisk::Root;
		root->setName("/");
		disks.append(root);

		struct passwd* pwd = getpwuid(getuid());
		BString path(pwd->pw_dir);
		BDisk* home = new BDisk();
		home->setName("Home");
		home->setAsset(path);
		disk_member(home)->type = BDisk::Symbol;
		//disk_member(home)->sectorsPerCluster;
		//disk_member(home)->bytesPerSector;
		//disk_member(home)->numberOfFreeClusters;
		//disk_member(home)->totalNumberOfClusters;
		disks.append(home);

		BString mountPath("/media/");
		mountPath += pwd->pw_name;
		BFolder folder(mountPath);
		if(folder.open())
		{
			for(int i = 0; i < folder.entryCount(); i++)
			{
				BEntry* entry = folder.entry(i);
				BDisk* disk = new BDisk();
				disk->setName(entry->name());
				disk->setAsset(entry->path());
				disk_member(disk)->type = BDisk::Symbol;
				//disk_member(disk)->sectorsPerCluster;
				//disk_member(disk)->bytesPerSector;
				//disk_member(disk)->numberOfFreeClusters;
				//disk_member(disk)->totalNumberOfClusters;
				disks.append(disk);
			}
		}
		/*
		FILE* fp = setmntent("/proc/mounts", "r");
		struct mntent* mentry = getmntent(fp);
		while(mentry)
		{
			const char* name = mentry->mnt_fsname;
			const char* path = mentry->mnt_dir;
			printf("The mount device: %s path: %s\n", name, path);
			BDisk* disk = new BDisk();
			disk->setName(name);
			//disk_member(disk)->type = type;
			//disk_member(disk)->sectorsPerCluster;
			//disk_member(disk)->bytesPerSector;
			//disk_member(disk)->numberOfFreeClusters;
			//disk_member(disk)->totalNumberOfClusters;
			disks.append(disk);
			mentry = getmntent(fp);
		}
		endmntent(fp);
		*/
		inited = true;
	}
	return disks;
}

BStringList BSystem::GetAllPortNames()
{
	BStringList portlst;
	
	return portlst;
}

bool BSystem::SetClipBoardText(const BString& text)
{
	return false;
}
bool BSystem::GetClipBoardText(BString& text)
{
	return text.size();
}

void BSystem::SetClipBoard(const BValue& value)
{
	app_clipBoard = value;
}
const BValue& BSystem::GetClipBoard()
{
	return	app_clipBoard;
}
void BSystem::CleanClipBoard()
{
	app_clipBoard.clear();
}

bool BSystem::KeyState(Key ckey)
{
	unsigned int state;
	switch (ckey)
	{
	case Key_LAlt:
		return 0;
	case Key_RAlt:
		return 0;
	case Key_LCtrl:
		return 0;
	case Key_RCtrl:
		return 0;
	case Key_LShift:
		return 0;
	case Key_RShift:
		return 0;
	default:
		return 0;
	}
	return 0;
}

BImage* BSystem::EntryImage(const BString& filePath)
{
	static BMap<int, BIconHolder> entryIconMap;
	//auto it = entryIconMap.find(finfo.iIcon);
	//if (it.valid())
	//	return *it;
	BImage* image = 0;
	//entryIconMap[finfo.iIcon] = icon;
	return image;
}
BIcon* BSystem::EntryIcon(const BString& filePath)
{

	static BMap<int, BIconHolder> entryIconMap;
	//auto it = entryIconMap.find(finfo.iIcon);
	//if (it.valid())
	//	return *it;
	BIcon* icon = 0;
	//entryIconMap[finfo.iIcon] = icon;
	return icon;
}

BImage* BSystem::TypedImage(StyleRole styleRole)
{
	return 0;
}
BIcon* BSystem::TypedIcon(StyleRole styleRole)
{
	int sign = 0;
	switch (styleRole)
	{
	case Icon_Title:
		//sign = IDI_WINLOGO;
		break;
	case Icon_Abount:
		//sign = IDI_APPLICATION;
		break;
	case Icon_Critical:
		//sign = IDI_ERROR;
		break;
	case Icon_Information:
		//sign = IDI_INFORMATION;
		break;
	case Icon_Question:
		//sign = IDI_QUESTION;
		break;
	case Icon_Warning:
		//sign = IDI_WARNING;
		break;
	default:
		return 0;
	}

	static BMap<int, BIconHolder> typedIconMap;
	//auto it = typedIconMap.find(hIcon);
	//if (it.valid())
	//	return *it;
	BIcon* icon = 0;
	//typedIconMap[hIcon] = icon;
	return icon;
}

#endif

#ifdef _WIN32
#include <Windows.h>

const BArray<BScreenHolder>& GetSystemScreens()
{
	static BArray<BScreenHolder> screens;
	static bool inited = false;
	if (!inited)
	{
		int count = ::GetSystemMetrics(SM_CMONITORS);
		for (int i = 0; i < count; i++)
		{
			DISPLAY_DEVICEA device;
			device.cb = sizeof(device);
			if (!::EnumDisplayDevicesA(NULL, i, &device, 0))// Do not track devices used for remote access (Terminal Services pseudo-displays, etc.)
				break;

			if (device.StateFlags & DISPLAY_DEVICE_MIRRORING_DRIVER)// Only return display devices that are attached to the desktop
				continue;

			if (!(device.StateFlags & DISPLAY_DEVICE_ATTACHED_TO_DESKTOP))
				continue;

			DEVMODEA deviceMode;
			if (!EnumDisplaySettingsA(device.DeviceName, ENUM_CURRENT_SETTINGS, &deviceMode))
			{
				printf("Error %ud : GetSystemScreens() - Unable to query information for screen number %d\n", ::GetLastError(), i);
				continue;
			}
			BScreen* screen = new BScreen();
			screen->setName(device.DeviceName);
			screen_member(screen)->info = device.DeviceString;
			screen_member(screen)->number = i;
			if ((deviceMode.dmFields & (DM_PELSWIDTH | DM_PELSHEIGHT)) != 0)
			{
				screen_member(screen)->pos.x() = deviceMode.dmPosition.x;
				screen_member(screen)->pos.y() = deviceMode.dmPosition.y;
				screen_member(screen)->size.setWidth(deviceMode.dmPelsWidth);
				screen_member(screen)->size.setHeight(deviceMode.dmPelsHeight);
				screen_member(screen)->pixelBits = deviceMode.dmBitsPerPel;
				//printf("screen %d : %d, %d, %d, %d\n", i, deviceMode.dmPosition.x, deviceMode.dmPosition.y, deviceMode.dmPelsWidth, deviceMode.dmPelsHeight);
			}
			else
			{
				screen_member(screen)->pos.x() = 0;
				screen_member(screen)->pos.y() = 0;
				screen_member(screen)->size.setWidth(0);
				screen_member(screen)->size.setHeight(0);
				screen_member(screen)->pixelBits = 0;
			}
			screens.append(screen);
		}
		inited = true;
	}
	return screens;
}
const BArray<BDiskHolder>& GetSystemDisks()
{
	static BArray<BDiskHolder> disks;
	static bool inited = false;
	if (!inited)
	{
		inited = true;
		TCHAR buffer[MAX_PATH] = {};
		GetLogicalDriveStrings(MAX_PATH, buffer);
		TCHAR* name = buffer;
		while (*name != 0)
		{
			int dtype = GetDriveType(name);
			BDisk::Type type = BDisk::Unknown;
			switch (dtype)
			{
			case DRIVE_REMOVABLE:
				type = BDisk::Removable;
				break;
			case DRIVE_FIXED:
				type = BDisk::Fixed;
				break;
			case DRIVE_REMOTE:
				type = BDisk::Remote;
				break;
			case DRIVE_CDROM:
				type = BDisk::CDRom;
				break;
			case DRIVE_RAMDISK:
				type = BDisk::RamDisk;
				break;
			default:
				break;
			}
			//printf("%s : %s\n", name, typeText);
			BDisk* disk = new BDisk();
			disk->setName(name);
			disk_member(disk)->type = type;
			DWORD& a = disk_member(disk)->sectorsPerCluster;
			DWORD& b = disk_member(disk)->bytesPerSector;
			DWORD& c = disk_member(disk)->numberOfFreeClusters;
			DWORD& d= disk_member(disk)->totalNumberOfClusters;
			GetDiskFreeSpace(name, &a, &b, &c, &d);
			disks.append(disk);
			name += strlen(name) + 1;
		}
	}
	return disks;
}
BImage* ExtractSytemImage(HICON hIcon)
{
	ICONINFO info = { 0 };
	if (!::GetIconInfo(hIcon, &info))
		return 0;
	BITMAP bitmap = { 0 };
	if (GetObject(info.hbmColor, sizeof(BITMAP), &bitmap) == 0)
	{
		DeleteObject(info.hbmColor);
		DeleteObject(info.hbmMask);
		return 0;
	}
	if (bitmap.bmBitsPixel == 32)
	{
		LONG size = bitmap.bmWidth * bitmap.bmHeight;
		bitmap.bmWidthBytes;
		BImage* image = new BImage(bitmap.bmWidth, bitmap.bmHeight, Format_RGBA);
		BColor* colors = (BColor*)image->pixels()->data();
		::GetBitmapBits(info.hbmColor, size * sizeof(BColor), colors);
		for (int i = 0; i < size; i++)
		{
			BColor& c = colors[i];
			bSwap(c.r(), c.b());
		}
		return image;
	}
	DeleteObject(info.hbmColor);
	DeleteObject(info.hbmMask);
	return 0;
}

int BSystem::GetCupNumber()
{
	static int CpuNumber = 0;
	SYSTEM_INFO SystemInfo;
	GetSystemInfo(&SystemInfo);
	CpuNumber = SystemInfo.dwNumberOfProcessors;
	return CpuNumber;
}

BStringList BSystem::GetAllPortNames()
{
	BStringList portlst;
	HKEY hkey = 0;
	if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, TEXT("HARDWARE\\DEVICEMAP\\SERIALCOMM"), 0, KEY_READ, &hkey) != ERROR_SUCCESS)
	{
		return portlst;
	}
	char valueName[256];
	DWORD nameSize = 256;
	char data[256];
	DWORD dataSize = 256;
	DWORD type = 0;
	int i = 0;
	while (ERROR_NO_MORE_ITEMS != RegEnumValue(hkey, i++, valueName, &nameSize, 0, &type, (BYTE*)data, &dataSize))
	{
		portlst.append(data);
		nameSize = 256;
		dataSize = 256;
	}
	RegCloseKey(hkey);
	return portlst;
}

bool BSystem::SetClipBoardText(const BString& text)
{
	if (!OpenClipboard(NULL))
		return false;

	if (!EmptyClipboard())
		return false;

	HGLOBAL hMen = GlobalAlloc(GMEM_MOVEABLE, text.size() + 1);
	if (!hMen)
	{
		CloseClipboard();
		return false;
	}

	if (LPSTR lpStr = (LPSTR)GlobalLock(hMen))
	{
		memcpy(lpStr, text.cstr(), text.size() + 1);
		GlobalUnlock(hMen);
		SetClipboardData(CF_TEXT, hMen);
	}

	CloseClipboard();
	return true;
}
bool BSystem::GetClipBoardText(BString& text)
{
	if (OpenClipboard(0))
	{
		UINT format = 0;
		while (1)
		{
			format = EnumClipboardFormats(format);
			if (format == CF_TEXT)
			{
				HGLOBAL hMem = GetClipboardData(format);
				SIZE_T dwSize = GlobalSize(hMem);
				char* cstr = (char*)::GlobalLock(hMem);
				if (cstr)
				{
					text = cstr;
					break;
				}
				GlobalUnlock(hMem);
			}
			if (format == 0)
				break;
		}
		CloseClipboard();
		return !text.empty();
	}
	return false;
}

void BSystem::SetClipBoard(const BValue& value)
{
	app_clipBoard = value;
}
const BValue& BSystem::GetClipBoard()
{
	return app_clipBoard;
}
void BSystem::CleanClipBoard()
{
	app_clipBoard.clear();
}

bool BSystem::KeyState(Key ckey)
{
	if (ckey == Key_RButton)
	{
		int state = GetKeyState(VK_RBUTTON);
		return state < 0;
	}
	if (ckey == Key_LButton)
	{
		int state = GetKeyState(VK_LBUTTON);
		return state < 0;
	}
	if (ckey == Key_MButton)
	{
		int state = GetKeyState(VK_MBUTTON);
		return state < 0;
	}
	if (ckey == Key_LAlt)
	{
		int state = GetKeyState(VK_LMENU);
		return state < 0;
	}
	if (ckey == Key_RAlt)
	{
		int state = GetKeyState(VK_RMENU);
		return state < 0;
	}
	if (ckey == Key_LCtrl)
	{
		int state = GetKeyState(VK_LCONTROL);
		return state < 0;
	}
	if (ckey == Key_RCtrl)
	{
		int state = GetKeyState(VK_RCONTROL);
		return state < 0;
	}
	if (ckey == Key_LShift)
	{
		int state = GetKeyState(VK_LSHIFT);
		return state < 0;
	}
	if (ckey == Key_RShift)
	{
		int state = GetKeyState(VK_RSHIFT);
		return state < 0;
	}
	return false;
}

BImage* BSystem::EntryImage(const BString& filePath)
{
	SHFILEINFO finfo;
	ZeroMemory(&finfo, sizeof(SHFILEINFO));
	if (filePath.contain('/'))
	{
		BString path = filePath;
		path.replace("//", '/');
		path.replace('/', '\\');
		SHGetFileInfo(path.cstr(), 0, &finfo, sizeof(finfo), SHGFI_ICON | SHGFI_LARGEICON);
	}
	else
	{
		SHGetFileInfo(filePath.cstr(), 0, &finfo, sizeof(finfo), SHGFI_ICON | SHGFI_LARGEICON);
	}
	if (finfo.iIcon == 0)
		return 0;

	static BMap<int, BImageHolder> entryImageMap;
	auto it = entryImageMap.find(finfo.iIcon);
	if (it.valid())
		return *it;

	BImage* image = ExtractSytemImage(finfo.hIcon);
	entryImageMap[finfo.iIcon] = image;
	return image;
}
BIcon* BSystem::EntryIcon(const BString& filePath)
{
	SHFILEINFO finfo;
	ZeroMemory(&finfo, sizeof(SHFILEINFO));
	if (filePath.contain('/'))
	{
		BString path = filePath;
		path.replace("//", '/');
		path.replace('/', '\\');
		SHGetFileInfo(path.cstr(), 0, &finfo, sizeof(finfo), SHGFI_ICON | SHGFI_LARGEICON);
	}
	else
	{
		SHGetFileInfo(filePath.cstr(), 0, &finfo, sizeof(finfo), SHGFI_ICON | SHGFI_LARGEICON);
	}
	if (finfo.iIcon == 0)
		return 0;

	static BMap<int, BIconHolder> entryIconMap;
	auto it = entryIconMap.find(finfo.iIcon);
	if (it.valid())
		return *it;

	BImage* image = ExtractSytemImage(finfo.hIcon);
	if (image == 0)
		return 0;

	BIcon* icon = new BIcon(image);
	entryIconMap[finfo.iIcon] = icon;
	return icon;
}

BImage* BSystem::TypedImage(StyleRole styleRole)
{
	LPSTR sign = 0;
	switch (styleRole)
	{
	case Icon_Title:
		sign = IDI_WINLOGO;
		break;
	case Icon_About:
		sign = IDI_APPLICATION;
		break;
	case Icon_Critical:
		sign = IDI_ERROR;
		break;
	case Icon_Information:
		sign = IDI_INFORMATION;
		break;
	case Icon_Question:
		sign = IDI_QUESTION;
		break;
	case Icon_Warning:
		sign = IDI_WARNING;
		break;
	default:
		return 0;
	}
	HICON hIcon = LoadIconA(0, sign);
	static BMap<HICON, BImageHolder> typedImageMap;
	auto it = typedImageMap.find(hIcon);
	if (it.valid())
		return *it;

	BImage* image = ExtractSytemImage(hIcon);
	typedImageMap[hIcon] = image;
	return image;
}
BIcon* BSystem::TypedIcon(StyleRole styleRole)
{
	LPSTR sign = 0;
	switch (styleRole)
	{
	case Icon_Title:
		sign = IDI_WINLOGO;
		break;
	case Icon_About:
		sign = IDI_APPLICATION;
		break;
	case Icon_Critical:
		sign = IDI_ERROR;
		break;
	case Icon_Information:
		sign = IDI_INFORMATION;
		break;
	case Icon_Question:
		sign = IDI_QUESTION;
		break;
	case Icon_Warning:
		sign = IDI_WARNING;
		break;
	default:
		return 0;
	}
	HICON hIcon = LoadIconA(0, sign);
	static BMap<HICON, BIconHolder> typedIconMap;
	auto it = typedIconMap.find(hIcon);
	if (it.valid())
		return *it;
	
	BImage* image = ExtractSytemImage(hIcon);
	if (image == 0)
		return 0;

	BIcon* icon = new BIcon(image);
	typedIconMap[hIcon] = icon;
	return icon;
}

HBITMAP CopyScreenToBitmap(LPRECT lpRect)
{
	HDC hScrDC, hMemDC;
	HBITMAP hBitmap, hOldBitmap;
	int nX, nY, nX2, nY2;
	int nWidth, nHeight;
	int xScrn, yScrn;
	if (IsRectEmpty(lpRect))
	{
		return NULL;
	}

	hScrDC = CreateDC("DISPLAY", NULL, NULL, NULL);

	hMemDC = CreateCompatibleDC(hScrDC);

	nX = lpRect->left;
	nY = lpRect->top;
	nX2 = lpRect->right;
	nY2 = lpRect->bottom;

	xScrn = GetDeviceCaps(hScrDC, HORZRES);
	yScrn = GetDeviceCaps(hScrDC, VERTRES);

	if (nX < 0)
		nX = 0;
	if (nY < 0)
		nY = 0;
	if (nX2 > xScrn)
		nX2 = xScrn;
	if (nY2 > yScrn)
		nY2 = yScrn;
	nWidth = nX2 - nX;
	nHeight = nY2 - nY;

	hBitmap = CreateCompatibleBitmap(hScrDC, nWidth, nHeight);

	hOldBitmap = (HBITMAP)SelectObject(hMemDC, hBitmap);

	BitBlt(hMemDC, 0, 0, nWidth, nHeight, hScrDC, nX, nY, SRCCOPY);

	hBitmap = (HBITMAP)SelectObject(hMemDC, hOldBitmap);

	DeleteDC(hScrDC);
	DeleteDC(hMemDC);

	return hBitmap;
}

#endif

int BSystem::GetScreenNumber()
{
	const BArray<BScreenHolder>& screens = GetSystemScreens();
	return screens.size();
}
BScreen* BSystem::GetScreen(int index)
{
	const BArray<BScreenHolder>& screens = GetSystemScreens();
	if (index < 0)
	{
		BPoint origin(0, 0);
		for (int i = 0; i < screens.size(); i++)
		{
			BScreen* screen = screens[i];
			if (screen->pos() == origin)
				return screen;
		}
		return 0;
	}
	return screens(index);
}
BScreen* BSystem::GetScreen(const BPoint& pos)
{
	const BArray<BScreenHolder>& screens = GetSystemScreens();
	for (int i = 0; i < screens.size(); i++)
	{
		BScreen* screen = screens[i];
		BRect rect(screen->pos(), screen->size());
		if (rect.contain(pos))
			return screen;
	}
	return 0;
}
BScreen* BSystem::GetScreen(const BRect& rect)
{
	const BArray<BScreenHolder>& screens = GetSystemScreens();
	for (int i = 0; i < screens.size(); i++)
	{
		BScreen* screen = screens[i];
		BRect rect(screen->pos(), screen->size());
		if (rect.contain(rect.pos()))
			return screen;		
	}
	return 0;
}

int BSystem::GetDiskNumber()
{
	const BArray<BDiskHolder>& disks = GetSystemDisks();
	return disks.size();
}
BDisk* BSystem::GetDisk(int index)
{
	const BArray<BDiskHolder>& disks = GetSystemDisks();
	return disks[index];
}

BTime BSystem::LocalTime()
{
	time_t t;
	::time(&t);
	tm* ptm = localtime(&t);
	BTime time(ptm->tm_hour, ptm->tm_min, ptm->tm_sec);
	return time;
}
BDate BSystem::LocalDate()
{
	time_t t;
	::time(&t);
	tm* ptm = localtime(&t);
	BDate date(ptm->tm_year + 1900, ptm->tm_mon, ptm->tm_mday);
	return date;
}